package com.zhuyuan.auth.config;

import com.xkcoding.justauth.AuthRequestFactory;
import com.zhuyuan.auth.granter.CaptchaTokenGranter;
import com.zhuyuan.auth.granter.SmsCodeTokenGranter;
import com.zhuyuan.auth.granter.SocialTokenGranter;
import com.zhuyuan.auth.service.impl.UserDetailServiceImpl;
import com.zhuyuan.core.constant.Oauth2Constants;
import com.zhuyuan.redis.RedisService;
import com.zhuyuan.security.exception.WebResponseTranslator;
import com.zhuyuan.security.filter.CustomClientCredentialsTokenEndpointFilter;
import com.zhuyuan.security.handler.CustomAuthenticationEntryPoint;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.CompositeTokenGranter;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.web.SecurityFilterChain;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @Author: 张琳凯
 * @Description: 认证服务器配置类
 * @DateTime: 2025/1/6 20:42
 **/
@Configuration
@EnableAuthorizationServer
@RequiredArgsConstructor
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {

    //认证服务器中Manager
    private final AuthenticationManager authenticationManager;
    //TokenConfig中Services
    private final AuthorizationServerTokenServices tokenService;
    //DetailsService
    private final UserDetailServiceImpl userDetailService;
    //客户端
    private final ClientDetailsServiceImpl clientService;
    //redis
    private final RedisService redisService;
    //第三方认证：JustAuth
    private final AuthRequestFactory factory;


    /**
     * 相关配置
     * token 异常处理等在这里配置
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints
                //对用户进行认证和校验
                .authenticationManager(authenticationManager)
                //设置授权模式
                .tokenGranter(tokenGranter(endpoints,tokenService))
                //TokenConfig，指定TokenStore,用来存取token
                .tokenServices(tokenService)
                //允许Get Post
                .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
                //通过SecurityUserDetailService中的loadbyusername,获取数据库中的用户信息
                .userDetailsService(userDetailService)
                //认证异常处理
                .exceptionTranslator(new WebResponseTranslator());
    }

    /**
     * 配置客户端详细信息服务
     * 通过数据库获取客户端信息
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        //这个与传入的参数 clientID clientSecret,进行匹配验证
        clientService.setSelectClientDetailsSql(Oauth2Constants.SELECT_CLIENT_DETAIL_SQL);
        clientService.setFindClientDetailsSql(Oauth2Constants.FIND_CLIENT_DETAIL_SQL);
        clientService.setPasswordEncoder(new BCryptPasswordEncoder());
        clients.withClientDetails(clientService);
    }

    /**
     * 令牌访问端点安全策略
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        //拦截客户端异常
        CustomAuthenticationEntryPoint authenticationEntryPoint = new CustomAuthenticationEntryPoint();
        CustomClientCredentialsTokenEndpointFilter filter = new CustomClientCredentialsTokenEndpointFilter(security);
        filter.afterPropertiesSet();
        filter.setAuthenticationEntryPoint(authenticationEntryPoint);
        security.addTokenEndpointAuthenticationFilter(filter);
        //
        security
                .tokenKeyAccess("isAuthenticated()")
                .checkTokenAccess("isAuthenticated()")
        ;

    }

    /**
     * 添加授权模式
     **/
    private TokenGranter tokenGranter(final AuthorizationServerEndpointsConfigurer endpoints, AuthorizationServerTokenServices tokenServices) {

        List<TokenGranter> granters = new ArrayList<>(Collections.singletonList(endpoints.getTokenGranter()));
        // 短信验证码模式
        granters.add(new SmsCodeTokenGranter(authenticationManager, tokenServices, endpoints.getClientDetailsService(),
                endpoints.getOAuth2RequestFactory(), redisService));
        // 验证码模式
        granters.add(new CaptchaTokenGranter(authenticationManager, tokenServices, endpoints.getClientDetailsService(),
                endpoints.getOAuth2RequestFactory(), redisService));
        // 社交登录模式
        granters.add(new SocialTokenGranter(authenticationManager, tokenServices, endpoints.getClientDetailsService(),
                endpoints.getOAuth2RequestFactory(), redisService, factory));
        // 密码模式
        granters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory()));
        return new CompositeTokenGranter(granters);
    }

}
